//#define USE_DUNGEONBUDDY_DLL

using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using Styx;
using Styx.CommonBot;
using Styx.Helpers;
using Styx.Patchables;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Classic
#else
    using Bots.DungeonBuddy.Profiles;
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Classic
#endif
{
    public class UpperBlackrockSpire : Dungeon
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 330; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(-7522.93, -1232.999, 285.74); }
        }

        public override WoWPoint ExitLocation
        {
            get { return new WoWPoint(77.55, -223.18, 49.84); }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var p in units)
            {
                WoWUnit unit = p.Object.ToUnit();
                switch (unit.Entry)
                {
                    case 9818: // Blackhand Summoner
                    case 10442: // Chromatic Whelp
                        if (StyxWoW.Me.IsDps())
                            p.Score += 120;

                        break;
                }
                if ((unit.Entry == BlackhandSummonerId || unit.Entry == ChromaticWhelpId) && Me.IsRange())
                    p.Score += 1000;
                if ((unit.Entry == ChromaticDragonspawnId || unit.Entry == BlackhandDragonHandlerId) && Me.IsDps() && Me.IsMelee())
                    p.Score += 1000;
            }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                o =>
                {
                    var unit = o as WoWUnit;
                    if (unit == null) return false;
                    if (unit.Entry == ChromaticWhelpId && Me.IsMelee())
                    {
                        if (unit.Location.IsPointLeftOfLine(_hallOfBlackHandGateLeftLoc, _hallOfBlackHandGateRightLoc))
                            return false;
                        if (Me.IsDps())
                        {
                            var tank = ScriptHelpers.Tank;
                            return tank != null && unit.Location.Distance(tank.Location) > 15;
                        }
                    }
                    if (unit.Entry == WarchiefRendBlackhandId && unit.HasAura("Whirlwind") && Me.IsMelee() && Me.IsDps())
                        return true;
                    return false;
                });
        }

        #endregion

        private const uint BlackhandSummonerId = 9818;
        private const uint PyroguardEmberseerId = 9816;
        private const uint WarchiefRendBlackhandId = 10429;
        private const uint ChromaticWhelpId = 10442;
        private const uint ChromaticDragonspawnId = 10447;
        private const uint BlackhandDragonHandlerId = 10742;

        private const uint TheBeastId = 10430;
        private readonly WoWPoint _drakeTankSpot = new WoWPoint(149.3783, -420.6858, 110.4725);
        private readonly WoWPoint _hallOfBlackHandGateLeftLoc = new WoWPoint(192.1564, -409.6148, 110.8851);
        private readonly WoWPoint _hallOfBlackHandGateRightLoc = new WoWPoint(191.8866, -431.2092, 111.2402);

        private LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(9816, "Pyroguard Emberseer")]
        public Composite PyroguardEmberseerBehavior()
        {
            WoWUnit boss = null;
            AddAvoidObject(ctx => Me.IsRange() && !Me.IsCasting, 10, o => o.Entry == PyroguardEmberseerId && o.ToUnit().CurrentTargetGuid != Me.Guid && o.ToUnit().Combat);
            return new PrioritySelector(ctx => boss = ctx as WoWUnit);
        }


        [ObjectHandler(175244, "Emberseer In", ObjectRange = 120)]
        public Composite ClearHallToEmberSeerHandler()
        {
            // clear hall of argo.
            WoWGameObject door = null;
            return new PrioritySelector(
                ctx => door = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(u => u.IsValid && u.Entry == 175244),
                new Decorator(ctx => door.State == WoWGameObjectState.Ready, ScriptHelpers.CreateClearArea(() => new WoWPoint(185.2658, -314.7129, 76.92092), 100, u => true)));
        }

        [ObjectHandler(175706, "Blackrock Altar", ObjectRange = 36)]
        public Composite BlackrockAltarHandler()
        {
            WoWGameObject entranceDoor = null;
            WoWUnit emberseer = null;
            return new PrioritySelector(
                ctx =>
                {
                    entranceDoor = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(u => u.IsValid && u.Entry == 175244);
                    emberseer = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.IsValid && u.Entry == 9816);
                    return ctx;
                },
                new Decorator(
                    ctx => StyxWoW.Me.Z > 80f && entranceDoor.State == WoWGameObjectState.Active && emberseer != null && emberseer.HasAura(15282),
                    ScriptHelpers.CreateInteractWithObject(175706, 8)),
                // wait for boss to spawn
                new Decorator(
                    ctx => emberseer != null && emberseer.IsAlive && !emberseer.HasAura(15282) && Targeting.Instance.FirstUnit == null,
                    new PrioritySelector(
                        new Decorator(
                            ctx => StyxWoW.Me.IsTank() && emberseer.DistanceSqr > 4 * 4,
                            new Action(ctx => Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(emberseer.Location)))),
                        new Decorator(ctx => StyxWoW.Me.IsTank() && Targeting.Instance.FirstUnit == null && emberseer.DistanceSqr <= 4 * 4, new ActionAlwaysSucceed()))));
        }

        [ObjectHandler(175186, "Boss Gate", ObjectRange = 120)]
        public Composite DrakeHandler()
        {
            WoWUnit tank = null;
            WoWGameObject exitDoor = null;
            return new PrioritySelector(
                ctx => exitDoor = ObjectManager.GetObjectsOfType<WoWGameObject>().FirstOrDefault(u => u.IsValid && u.Entry == 164726),
                new Decorator(
                    ctx => ((WoWGameObject)ctx).State == WoWGameObjectState.Ready && !ScriptHelpers.IsBossAlive("Pyroguard Emberseer"),
                    new PrioritySelector(
                        new Decorator(
                            ctx => exitDoor.State == WoWGameObjectState.Ready,
                            new PrioritySelector(
                                ctx => tank = ScriptHelpers.Tank,
                // if we're a follow and stray too far from tank than move towards tank 
                                new Decorator(
                                    ctx => StyxWoW.Me.IsFollower() && tank != null && (tank.DistanceSqr > 15 * 15 || ObjectManager.GetObjectsOfType<WoWUnit>().Any(u => u.Aggro)),
                                    new Action(
                                        ctx => Navigator.GetRunStatusFromMoveResult(Navigator.MoveTo(WoWMathHelper.CalculatePointFrom(StyxWoW.Me.Location, tank.Location, 4f))))),
                // tank shit at one spot, don't get trapped on other side of the drake gate with no healer. 
                                new Decorator(
                                    ctx => StyxWoW.Me.IsTank(),
                                    new PrioritySelector(
                                        ScriptHelpers.CreateTankUnitAtLocation(() => _drakeTankSpot, 20f),
                                        new Decorator(
                // don't do anything while waiting for the waves.
                                            ctx => Targeting.Instance.IsEmpty(), new ActionAlwaysSucceed()))))))));
        }

        [EncounterHandler(10339, "Gyth")]
        public Composite GythEncounter()
        {
            WoWUnit boss = null;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateDispellEnemy("Chromatic Chaos", ScriptHelpers.EnemyDispellType.Magic, () => boss),
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => !Me.IsTank() && boss.Distance < 15 && boss.CurrentTargetGuid != Me.Guid, () => boss, new ScriptHelpers.AngleSpan(0, 180)),
                ScriptHelpers.CreateTankFaceAwayGroupUnit(15));
        }

        [EncounterHandler(10429, "Warchief Rend Blackhand")]
        public Composite WarchiefRendBlackhandEncounter()
        {
            WoWUnit warchief = null;
            AddAvoidObject(ctx => !Me.IsTank(), 8, o => o.Entry == WarchiefRendBlackhandId && o.ToUnit().HasAura("Whirlwind"));

            return new PrioritySelector(
                ctx => warchief = ctx as WoWUnit,
                ScriptHelpers.CreateTankFaceAwayGroupUnit(8),
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => !Me.IsTank() && warchief.Distance < 8 && warchief.CurrentTargetGuid != Me.Guid, () => warchief, new ScriptHelpers.AngleSpan(0, 180)));
        }

        [EncounterHandler(10430, "The Beast")]
        public Composite TheBeastEncounter()
        {
            AddAvoidObject(ctx => Me.IsRange() && !Me.IsCasting, 10, o => o.Entry == TheBeastId && o.ToUnit().CurrentTargetGuid != Me.Guid && o.ToUnit().Combat);

            return new PrioritySelector(
                // tank against wall because of knockback.
                ScriptHelpers.CreateTankUnitAtLocation(() => new WoWPoint(70.44568, -541.3387, 110.9312), 5f));
        }


        [EncounterHandler(10363, "General Drakkisath")]
        public Composite GeneralDrakkisathEncounter()
        {
            WoWUnit boss = null;
            const uint flameStrikeId = 16419;
            AddAvoidObject(ctx => true, o => Me.IsRange() && Me.IsMoving ? 6 : 5, o => o is WoWPlayer && o.ToPlayer().HasAura("Conflagration") && !o.IsMe);
            AddAvoidObject(ctx => true, 5, flameStrikeId);

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => !Me.IsTank() && boss.Distance < 8 && boss.CurrentTargetGuid != Me.Guid, () => boss, new ScriptHelpers.AngleSpan(0, 180)),
                ScriptHelpers.CreateTankFaceAwayGroupUnit(8));
        }
    }
}